#pragma once

#include "viewer_dc.h"
#include "ViewerWnd.h"
#include "common/com_impl_ptr.h"

#include <boost/optional.hpp>

namespace viewer
{
   struct ATL_NO_VTABLE ViewerImpl : 
       CComObjectRootEx<CComMultiThreadModel>,
       IViewerWindowEvent
   {
       HWND window_handler_;
       ViewerImpl(char const *label) 
          : label_ (label) 
       {
       }

       ViewerImpl(char const *label, RECT const &rect)
          : label_(label)
          , rect_ (rect)
       {
       }

       void DoModal ()
       {
           IViewerWindow2DPtr wnd ( __uuidof(ViewerWindow2D) ) ; 
           dc_ = wnd ;

           wnd -> Advise ( this ) ; 
           wnd -> DoModal ( NULL, label_.c_str(), rect_.is_initialized() ? &rect_.get() : NULL ) ; 
           wnd -> Unadvise () ; 
       }
       void MakeControl ()
       {
           IViewerWindow2DPtr wnd ( __uuidof(ViewerWindow2D) ) ; 
           dc_ = wnd ;

           //wnd -> Advise ( this ) ; 
           window_handler_ = wnd -> Create(NULL,  label_.c_str(), rect_.is_initialized() ? &rect_.get() : NULL);
           //DoModal ( NULL, label_.c_str(), rect_.is_initialized() ? &rect_.get() : NULL ) ; 
           printf("hwnd: %d \n", window_handler_);
           //wnd -> Unadvise ();
       }

       BEGIN_COM_MAP(ViewerImpl)
           COM_INTERFACE_ENTRY(IViewerWindowEvent)
       END_COM_MAP()

       void __stdcall OnDraw   () 
       {
       }

       void __stdcall OnMove   ( BOOL * redraw ) 
       {
           *redraw = TRUE;
       }

       BOOL __stdcall OnButton ( BOOL, BOOL, BOOL * )
       {
          return FALSE;
       } 

       BOOL __stdcall OnKey    ( DWORD, BOOL, BOOL * )
       {
          return FALSE;
       }

       void __stdcall OnDestroy()
       {

       }

   private:
       std::string           label_;
       boost::optional<RECT> rect_;

   protected:
       viewer_dc   dc_;
   };

   template < class Viewer >
   void run_viewer()
   { 
      CoInitialize( 0 ); 
#pragma omp critical (run_viewer)
      com_impl_obj< Viewer >()->DoModal();
   }

   template< class Viewer, typename P1>
   void run_viewer( P1 const & p1 )
   {
      CoInitialize( 0 );
#pragma omp critical (run_viewer)
      com_impl_obj< Viewer >( p1 )->DoModal();
   } 

   template< class Viewer, typename P1, typename P2>
   void run_viewer( P1 const& p1, P2 const& p2 )
   {
      CoInitialize( 0 );
#pragma omp critical (run_viewer)
      com_impl_obj< Viewer >( p1, p2 )->DoModal();
   } 

   template< class Viewer, typename P1, typename P2, typename P3>
   void run_viewer( P1 const& p1, P2 const& p2, P3 const & p3 )
   {
      CoInitialize( 0 );
#pragma omp critical (run_viewer)
      com_impl_obj< Viewer >( p1, p2, p3 )->DoModal();
   } 

   template< class Viewer, typename P1, typename P2, typename P3, typename P4 >
   void run_viewer( P1 const& p1, P2 const& p2, P3 const & p3, P4 const & p4 )
   {
      CoInitialize( 0 );
#pragma omp critical (run_viewer)
      com_impl_obj< Viewer >( p1, p2, p3, p4 )->DoModal();
   } 
}